package websocket

import (
	"encoding/json"
	"fmt"
	"golang.org/x/net/websocket"
	"net/http"
	"time"
)

const (
	NORMAL int = iota
	CLOSE
)

var singServer *Server

var (
	OnOpen      func(*Server, *websocket.Conn)
	OnMessage   func(*Server, *Frame)
	OnClose     func(*Server, *websocket.Conn)
	OnHandSheKe func(*websocket.Config, *http.Request) error // todo 自定义握手协议的方法
)

var (
	HeartbeatCheckInterval      = 0 // todo 定义一个心跳检测的时间范围， 如果是0则不检测
	Req                    byte = 0 // 正常数据 加在实际发送的数据上
	Heartbeat              byte = 1 // 代表的是心跳数据
)

// 封装Websocket
type Server struct {
	addr string
	uri  string
	conn *websocket.Conn
}

func NewServer(addr, uri string) *Server {
	singServer = &Server{
		addr: addr,
		uri:  uri,
	}

	if OnHandSheKe != nil {
		http.Handle(uri, websocket.Server{
			Handshake: OnHandSheKe,
			Handler:   websocket.Handler(server),
		})
		return singServer
	}

	http.Handle(uri, websocket.Handler(server))
	return singServer
}

// 获取 conn的方法
func (s *Server) Conn() *websocket.Conn {
	return s.conn
}

// 发送信息的方法
func (s *Server) Send(v interface{}) {
	data := append([]byte{Req}, marshal(v)...)
	websocket.Message.Send(s.conn, data)
}

// todo 发送方法
func SendJson(conn *websocket.Conn, v interface{}) {
	msg, _ := json.Marshal(v)
	d := append([]byte{Req}, msg...)
	websocket.Message.Send(conn, d)
}

func RecJson(conn *websocket.Conn, v interface{}) {
	var data []byte
	websocket.Message.Receive(conn, &data)
	json.Unmarshal(data[1:], v)
}

// 关闭链接的方法
func Close(conn *websocket.Conn) {
	defer conn.Close()
	h.c[conn] = false
	OnClose(singServer, conn)
}

// todo 执行服务的核心方法
func server(conn *websocket.Conn) {
	defer conn.Close()
	// 判断是否设置心跳检测
	if HeartbeatCheckInterval > 0 {
		go heartbeat(conn) // todo 要采用协程方式运行，要不会阻塞
	}
	h.c[conn] = true
	// 设置启动方法
	singServer.conn = conn
	// 接收数据的变量
	var message []byte
	frame := new(Frame)

	OnOpen(singServer, conn)
	for {
		err := websocket.Message.Receive(conn, &message)
		if err != nil {
			if h.c[conn] {
				fmt.Println("====》》》 主动断开 ===》》》》")
				if HeartbeatCheckInterval > 0 {
					h.heartbeatCh <- CLOSE
				}
				// 断开链接
				Close(conn)
			}

			break
		}
		if HeartbeatCheckInterval > 0 {
			h.heartbeatCh <- NORMAL
		}
		// 接收参数，处理
		frame.Data = string(message[1:])
		OnMessage(singServer, frame)
	}
}

// todo 心跳机制的方法
func heartbeat(conn *websocket.Conn) {
	t := time.Duration(HeartbeatCheckInterval) * time.Second
	timer := time.NewTicker(t)
	for {
		select {
		case f := <-h.heartbeatCh: // todo 接收心跳包的数据
			if f == NORMAL {
				timer.Reset(t) // todo 重置时间
			}
			if f == CLOSE {
				fmt.Println("====>>>> 主动关闭连接 ====>>>")
				timer.Stop()
			}
		case <-timer.C: // todo 一段时间内没有收到心跳包时的处理
			fmt.Println("===>>> 心跳断开链接 ===>>>")
			Close(conn)
			timer.Stop()
			return
		}
	}
}

func marshal(v interface{}) (msg []byte) {
	switch data := v.(type) {
	case string:
		return []byte(data)
	case []byte:
		return data
	}
	return nil
}
